#pragma GCC optimize("O0")

#include <arch/io.h>

void mmio_write8(uintptr_t base, ptrdiff_t byte_off, uint8_t val) {
	*((volatile uint8_t *)(base + byte_off)) = val;
}

void mmio_write16(uintptr_t base, ptrdiff_t byte_off, uint16_t val) {
	*((volatile uint16_t *)(base + byte_off)) = val;
}

void mmio_write32(uintptr_t base, ptrdiff_t byte_off, uint32_t val) {
	*((volatile uint32_t *)(base + byte_off)) = val;
}

void mmio_write64(uintptr_t base, ptrdiff_t byte_off, uint64_t val) {
	*((volatile uint64_t *)(base + byte_off)) = val;
}

uint8_t mmio_read8(uintptr_t base, ptrdiff_t byte_off) {
	return *((volatile uint8_t *)(base + byte_off));
}

uint16_t mmio_read16(uintptr_t base, ptrdiff_t byte_off) {
	return *((volatile uint16_t *)(base + byte_off));
}

uint32_t mmio_read32(uintptr_t base, ptrdiff_t byte_off) {
	return *((volatile uint32_t *)(base + byte_off));
}

uint64_t mmio_read64(uintptr_t base, ptrdiff_t byte_off) {
	return *((volatile uint64_t *)(base + byte_off));
}

void io_write8(uint16_t port, uint8_t val) {
	__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}

void io_write16(uint16_t port, uint16_t val) {
	__asm__ volatile("outw %0, %1" : : "a"(val), "Nd"(port));
}

void io_write32(uint16_t port, uint32_t val) {
	__asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port));
}

uint8_t io_read8(uint16_t port) {
	uint8_t val;
	__asm__ volatile("inb %1, %0" : "=a"(val) : "Nd"(port));
	return val;
}

uint16_t io_read16(uint16_t port) {
	uint16_t val;
	__asm__ volatile("inw %1, %0" : "=a"(val) : "Nd"(port));
	return val;
}

uint32_t io_read32(uint16_t port) {
	uint32_t val;
	__asm__ volatile("inl %1, %0" : "=a"(val) : "Nd"(port));
	return val;
}

void io_wrmsr(uint32_t msr, uint64_t val) {
	__asm__ volatile("wrmsr" : : "a"(val & 0xFFFFFFFF), "d"(val >> 32), "c"(msr));
}

uint64_t io_rdmsr(uint32_t msr) {
	uint32_t low, high;
	__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
	return (uint64_t)high << 32 | low;
}